﻿using System;
using Core.Model;
using Core.Model.Blocks;
using Core.Model.Chunks;
using Core.Model.Types;
using Core.View.Builders.Terrains.Base;

namespace Core.View.Builders.Terrains
{
    public class TerrainValleysForRiver:SimpleTerrain
    {
        public const int SANDLEVEL = 58;

        public const int VALLEYWATERLEVEL = WATERLEVEL + 3;

        #region generateTerrain

        public override BlockType GetBlockType(VectorIndex position, byte higerPlant, byte lowerPlant, int x, int y, int z, bool isSunlite)
        {
            byte upperGroundHeight = higerPlant;
            byte lowerGroundHeight = lowerPlant;

            uint worldX = (uint)(position.X + x + World.SEED);
            uint worldZ = (uint)(position.Z + z);

            if (y > upperGroundHeight)
            {
                if (y <= VALLEYWATERLEVEL)
                {
                    return BlockType.Water;
                }

                return BlockType.None;
            }
            else if (y > lowerGroundHeight) // Are we above the lower ground height?
            {
                //Let's see about some caves er valleys!
                float caveNoise = PerlinSimplexNoise.noise(worldX * 0.01f, worldZ * 0.01f, y * 0.01f) * (0.015f * y) + 0.1f;
                caveNoise += PerlinSimplexNoise.noise(worldX * 0.01f, worldZ * 0.01f, y * 0.1f) * 0.06f + 0.1f;
                //caveNoise += PerlinSimplexNoise.noise(worldX * 0.2f, worldZ * 0.2f, y * 0.2f) * 0.03f + 0.01f;

                // We have a cave, draw air here.
                if (caveNoise > 0.2f)
                {
                    if (y <= VALLEYWATERLEVEL)
                    {
                        return BlockType.Water;
                    }

                    return BlockType.None;
                }
            }

            if (y > SNOWLEVEL)
            {
                return BlockType.Snow;
            }

            if (y > VALLEYWATERLEVEL)
            {
                return BlockType.Grass;
            }
            else if (y >= VALLEYWATERLEVEL - 3)
            {
                return BlockType.Dirt;
            }
            else
            {
                return BlockType.Sand;
            }
        }

        public sealed override void GenerateGroundHeight(Chunk chunk, int blockXInChunk, int blockZInChunk, uint worldX, uint worldZ)
        {
            byte lowerGroundHeight = GetLowerGroundHeight(chunk, worldX, worldZ);
            byte upperGroundHeight = GetUpperGroundHeight(chunk, worldX, worldZ, lowerGroundHeight);

            chunk.UpperPlant[blockXInChunk, blockZInChunk] = upperGroundHeight;
            chunk.LowerPlant[blockXInChunk, blockZInChunk] = lowerGroundHeight;

            if (chunk.UpperGroundHeight < upperGroundHeight)
            {
                chunk.UpperGroundHeight = upperGroundHeight;
            }

            if (chunk.LowerGroundHeight > lowerGroundHeight - 1)
            {
                chunk.LowerGroundHeight = (byte)(lowerGroundHeight - 1);
            }
        }


        #endregion

        #region GetUpperGroundHeight
        private static byte GetUpperGroundHeight(Chunk chunk, uint blockX, uint blockY, byte lowerGroundHeight)
        {
            int minimumGroundheight = lowerGroundHeight;
            int minimumGroundDepth = (int)(Chunk.Size.Y * 0.5f);

            float octave1 = PerlinSimplexNoise.noise((blockX + 100) * 0.001f, blockY * 0.001f) * 0.5f;
            float octave2 = PerlinSimplexNoise.noise((blockX + 100) * 0.002f, blockY * 0.002f) * 0.25f;
            float octave3 = PerlinSimplexNoise.noise((blockX + 100) * 0.01f, blockY * 0.01f) * 0.25f;
            float octaveSum = Math.Min(octave1 + octave2 + octave3, Chunk.Size.Y - 1);

            return (byte)(octaveSum * minimumGroundheight + minimumGroundDepth);
        }
        #endregion

        #region GetLowerGroundHeight
        private static byte GetLowerGroundHeight(Chunk chunk, uint blockX, uint blockY)
        {
            int minimumGroundheight = Chunk.Size.Y / 4;
            int minimumGroundDepth = (int)(Chunk.Size.Y * 0.5f);

            float octave1 = PerlinSimplexNoise.noise(blockX * 0.0001f, blockY * 0.0001f) * 0.5f;
            float octave2 = PerlinSimplexNoise.noise(blockX * 0.0005f, blockY * 0.0005f) * 0.35f;
            float octave3 = PerlinSimplexNoise.noise(blockX * 0.02f, blockY * 0.02f) * 0.15f;
            float octaveSum = octave1 + octave2 + octave3;

            return (byte)(octaveSum * minimumGroundDepth + minimumGroundheight);
        }
        #endregion

    }
}
